package org.durandj.ystream.client.network;

import java.util.Collections;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.logging.Logger;

/**
 * TODO:
 * 
 * @author durandj
 * @version 1.0
 *
 */
public abstract class PacketHandler
{
	/**
	 * This is where the packets are stored while
	 * they are waiting to be handled. All packets are
	 * sorted by their priority so that high priority packets
	 * are stored towards the front of the queue.
	 * 
	 * @author durandj
	 * @version 1.0
	 *
	 */
	private class PacketQueue
	{
		/**
		 * The packet queue.
		 */
		LinkedList<Packet> queue;
		
		/**
		 * Creates an empty packet queue.
		 */
		public PacketQueue()
		{
			queue = new LinkedList<Packet>();
		}
		
		/**
		 * Queues a packet up.
		 * 
		 * @param packet - the packet
		 */
		public synchronized void queue(Packet packet)
		{
			if(packet == null)
				throw new NullPointerException("Must give a valid packet.");
			
			queue.add(packet);
			Collections.sort(queue);
		}
		
		/**
		 * Dequeues a packet from the queue.
		 * 
		 * @return the first item in the queue
		 */
		public synchronized Packet dequeue()
		{
			if(queue.isEmpty())
				throw new NoSuchElementException("No more elements in the queue.");
			
			return queue.remove();
		}
		
		/**
		 * Checks if the queue is empty.
		 * 
		 * @return is it empty?
		 */
		public boolean isEmpty()
		{
			return queue.isEmpty();
		}
	}
	
	/**
	 * Logs packets.
	 */
	protected Logger log = Logger.getLogger(this.getClass().getName());
	// TODO: This will need to be redirected to a log file instead of the console.
	
	/**
	 * The packet queue.
	 */
	private PacketQueue packets;
	
	/**
	 * Creates a packet handler with an empty packet queue.
	 */
	public PacketHandler()
	{
		packets = new PacketQueue();
	}
	
	/**
	 * Queues a packet up for the packet handler to take care of.
	 * 
	 * @param packet - the packet
	 */
	public void queuePacket(Packet packet)
	{
		log.info("Packet received...\n" +
		         "\tPriority: " + packet.getPriority() + "\n" +
		         "\tOpcode: " + packet.getOpcode() + "\n" +
		         "\tSender: " + packet.getSource());
		
		synchronized(packets) {
			packets.queue(packet);
			packets.notifyAll();
		}
	}
	
	/**
	 * Gets the next packet from the packet queue.
	 * 
	 * @return the next packet
	 */
	protected Packet nextPacket()
	{
		return packets.dequeue();
	}
	
	/**
	 * Checks if there are no packets in the packet queue.
	 * 
	 * @return are there any packets in the queue?
	 */
	protected boolean noPackets()
	{
		return packets.isEmpty();
	}
	
	/**
	 * Waits for a packet to be sent to the packet queue. This
	 * should only be called if the packet queue is empty.
	 */
	protected void waitForPackets()
	{
		synchronized(packets) {
			try
			{
				packets.wait();
			}
			catch(InterruptedException e)
			{}
		}
	}
	
	/**
	 * This is where all the work of the packet handler is done. A
	 * packet is pass in and something should be done to handle obviously.
	 * 
	 * @param packet - the current packet
	 */
	protected abstract void handlePacket(Packet packet);
}
